package com.idealighter.game.roomcard.common;

import com.idealighter.game.core.assertions.HuohuaAssert;
import com.idealighter.game.core.common.Game;
import com.idealighter.game.core.error.ErrorCode;
import com.idealighter.game.event.manager.EventMgr;
import com.idealighter.game.player.manager.PlayerMgr;
import com.idealighter.game.player.struct.Player;
import com.idealighter.game.roomcard.manager.RoomCardRoomListMgrScript;
import com.idealighter.game.roomcard.struct.RoomCardRoomCfg;
import com.idealighter.game.roomcard.struct.RoomCardRoomCfgValue;
import com.idealighter.game.roomcard.struct.RoomCardRoomListMap;
import com.idealighter.game.roomcard.struct.constant.RoomCardBillTypeConstant;
import com.idealighter.game.roomcard.struct.constant.RoomCardStateConstant;
import com.idealighter.game.roomcard.struct.convert.RoomCardRoomListConvert;
import com.idealighter.game.schedule.manager.ScheduleMgr;
import com.idealighter.game.server.ExecutorMgr;
import com.idealighter.game.server.context.ApplicationContext;
import com.idealighter.game.server.core.executor.DisruptorExecutor;
import com.idealighter.game.server.db.datadb.generate.domain.RoomCardRoomListDomain;
import com.idealighter.game.server.db.datadb.generate.domain.RoomCardRoomListDomainExample;
import com.idealighter.game.server.db.datadb.mapper.RoomCardRoomListMapper;
import com.idealighter.utils.check.EmptyUtil;
import com.idealighter.utils.json.JsonUtil;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

import org.mybatis.guice.transactional.Transactional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @ClassName AbstractRoomCardMgr.
 * @Description 房卡管理超类
 * @date 2018年5月9日 下午5:25:25 .
 */
public abstract class AbstractRoomCardGameMgr<//
    R extends AbstractRoomCardRoomWithTable<T, S, RCFGT, RCFGVT>, // ROOM
    T extends AbstractRoomCardTable<S, RCFGT, RCFGVT>, // table
    S extends AbstractRoomCardSeat<RCFGT, RCFGVT>, // seat
    RCFGT extends RoomCardRoomCfg<RCFGVT>, // 房间配置
    RCFGVT extends RoomCardRoomCfgValue> { // 房间配置值
  private static final Logger LOG = LoggerFactory.getLogger(AbstractRoomCardGameMgr.class);
  public final RoomCardRoomListMapper roomCardRoomListMapper;
  // private final EventMgr eventMgr;
  public final DisruptorExecutor gameExecutor;
  // 所属游戏
  public final Game game;
  public final RoomCardRoomListMgrScript roomCardRoomListMgrScript;
  // public final RoomCardGameCfgMgrScript roomCardGameCfgMgrScript;
  private static final Long unStartExpireTime = 15 * 60 * 1000L; // 游戏未开始过期时间(15分钟)
  private Map<Long, ScheduledFuture<?>> expireScheduleMap = new HashMap<Long, ScheduledFuture<?>>(); // 过期定时清除事件
  private final ScheduleMgr scheduleMgr;

  // 玩家与座位的映射
  private final Map<Long, S> playerSeats = new HashMap<>();
  // 游戏中的房间(房卡列表)
  private final Map<Long, R> rooms = new HashMap<Long, R>();

  /**
   * 构造函数.
   */
  public AbstractRoomCardGameMgr(Game game, EventMgr eventMgr, ExecutorMgr executorMgr,
      ScheduleMgr scheduleMgr, RoomCardRoomListMapper roomCardRoomListMapper,
      RoomCardRoomListMgrScript roomCardRoomListMgrScript) {
    this.game = game;
    this.roomCardRoomListMapper = roomCardRoomListMapper;
    this.gameExecutor = executorMgr.getGameExecutor(game.getModuleId());
    this.scheduleMgr = scheduleMgr;
    this.roomCardRoomListMgrScript = roomCardRoomListMgrScript;
    // this.roomCardGameCfgMgrScript = roomCardGameCfgMgrScript;
    // this.eventMgr = eventMgr;
    eventMgr.register(this);

    // 加载配置数据
    try {
      loadData();
    } catch (Exception e) {
      LOG.error("[{}]获取房卡房间列表失败", game.getDesc(), e);
      System.exit(0);
    }
  }

  private void loadData() throws InstantiationException, IllegalAccessException {
    RoomCardRoomListDomainExample example = new RoomCardRoomListDomainExample();
    example.setOrderByClause("id asc");
    RoomCardRoomListDomainExample.Criteria criteria = example.createCriteria();
    criteria.andGameIdEqualTo(game.getType());
    criteria.andStateEqualTo(RoomCardStateConstant.NO_START);

    // 获取指定游戏的房卡(正在进行)
    List<RoomCardRoomListDomain> list = roomCardRoomListMapper.selectByExampleWithBLOBs(example);

    if (EmptyUtil.listIsNotEmpty(list)) {
      for (RoomCardRoomListDomain domain : list) {
        RoomCardRoomCfg<? extends RoomCardRoomCfgValue> bo =
            RoomCardRoomListMap.findRoomCfgByGame(domain.getGameId()).newInstance();
        RoomCardRoomListConvert.INSTANCE.po2bo(bo, domain);
        // 创建房间并添加到房间列表
        @SuppressWarnings("unchecked")
        R room = roomCfgToRoom((RCFGT) bo);
        addRoomCard(room);
      }
    }
  }

  /**
   * addRoomCard.
   * 
   * @Description 添加房卡
   * @date 2018年5月21日 下午2:00:05
   * @param room .
   */
  private void addRoomCard(R room) {
    rooms.put(room.getId(), room);
    roomCardRoomListMgrScript.addRoomCard(room);

    // 定时退出
    ScheduledFuture<?> expireSchedule = expireScheduleMap.remove(room.getId());
    if (expireSchedule != null) {
      expireSchedule.cancel(false);
    }
    expireSchedule = scheduleMgr.schedule(() -> doUnStartExipre(room), unStartExpireTime,
        TimeUnit.MILLISECONDS, gameExecutor);
    expireScheduleMap.put(room.getId(), expireSchedule);

    LOG.info("AbstractRoomCardGameMgr添加房卡[{}]: [{}]", room.getId(), JsonUtil.toJson(room));
  }

  private void doUnStartExipre(R room) {
    if (room != null) {
      unStartExpire(room);
      // 游戏结束
      gameOver(room);
    }
  }

  public void releasePlayerRoomTableSeat(Player player) {
    Long playerId = player.getId();
    S seat = removePlayerSeat(playerId);
    player.curRoom.getPlayers().remove(playerId);
    seat.clear();
    if (player != null) {
      player.curRoom = null;
      player.curTable = null;
      player.curSeat = null;
    }
  }

  /**
   * removeRoomCard.
   * 
   * @Description 移除房卡
   * @date 2018年5月21日 下午2:05:59
   * @param roomId .
   */
  private void removeRoomCard(Long roomId) {
    R room = rooms.remove(roomId);
    roomCardRoomListMgrScript.removeRoomCard(roomId);

    // 移除玩家
    PlayerMgr playerMgr = ApplicationContext.getBean(PlayerMgr.class);
    Set<Long> players = room.getPlayers();
    if (players != null && players.size() > 0) {
      List<Long> tempIds = new ArrayList<>(room.getPlayers());
      for (int i = room.getPlayers().size() - 1; i >= 0; i--) {
        Long playerId = tempIds.get(i);
        Player player = playerMgr.getPlayer(playerId);
        releasePlayerRoomTableSeat(player);
      }
    }

    // 移除房间
    ScheduledFuture<?> expireSchedule = expireScheduleMap.remove(roomId);
    if (expireSchedule != null) {
      expireSchedule.cancel(false);
    }
    LOG.info("AbstractRoomCardGameMgr移除房卡[{}]: [{}]", roomId, room != null ? room.getId() : null);

    room = null;
  }

  /**
   * @Title createRoomCard.
   * @Description 创建房卡
   * @date 2018年5月21日 下午3:00:37
   * @param roomCfg.
   */
  @Transactional
  public R createRoomCard(RCFGT roomCfg) {
    HuohuaAssert.isTrue(roomCfg != null, ErrorCode.ADD_ROOMCARD_FAIL);
    // 转换成domain
    RoomCardRoomListDomain domain = RoomCardRoomListConvert.INSTANCE.bo2po(roomCfg);
    HuohuaAssert.isTrue(domain != null, ErrorCode.ADD_ROOMCARD_FAIL);
    // domain保存到数据库
    int count = roomCardRoomListMapper.insertSelective(domain);
    HuohuaAssert.isTrue(count > 0, ErrorCode.ADD_ROOMCARD_FAIL);
    // 从数据库中获取到id
    roomCfg.setId(domain.getId());
    // 创建房间实例
    R room = roomCfgToRoom(roomCfg);
    Long roomId = room.getId();
    if (roomId == null || !room.getId().equals(roomCfg.getId())) {
      LOG.error("房卡房号[{}]不为给的房号[{}]", roomId, roomCfg.getId());
      HuohuaAssert.isTrue(false);
    }

    // 添加到房间列表
    addRoomCard(room);
    return room;
  }

  /**
   * @Title roundStart.
   * @Description 一轮游戏开始
   * @date 2018年5月21日 下午4:25:07
   * @param room .
   */
  public void roundStart(R room) {
    // 定时器取消
    ScheduledFuture<?> schedule = expireScheduleMap.remove(room.getId());
    if (schedule != null) {
      schedule.cancel(false);
    }
    RCFGT roomCfg = room.getRoomCfg();
    Byte state = roomCfg.getState();
    // 维护状态(未开始->开始)
    if (state.equals(RoomCardStateConstant.NO_START)) {
      roomCfg.setState(RoomCardStateConstant.GOING);
      roomCfg.setStarttime(new Date());

      // 更新配置到数据库
      RoomCardRoomListDomain record = RoomCardRoomListConvert.INSTANCE.bo2po(roomCfg);
      roomCardRoomListMapper.updateByPrimaryKeySelective(record);
    }
    // 扣除剩余局数
    if (roomCfg.getBillType() == RoomCardBillTypeConstant.BY_GAME) {
      roomCfg.setLeftGame(roomCfg.getLeftGame() - 1);
    } else if (roomCfg.getBillType() == RoomCardBillTypeConstant.BY_TIME) {
      room.setRoundStartTime(new Date());
    }
    roomCfg.setPlayGames(roomCfg.getPlayGames() + 1);
  }

  /**
   * @Title roundEnd.
   * @Description 一轮游戏结束
   * @date 2018年5月21日 下午4:25:22
   * @param room .
   * @return 整个游戏结束时返回true
   */
  public boolean roundEnd(R room) {
    RCFGT roomCfg = room.getRoomCfg();
    Byte state = roomCfg.getState();

    boolean isOver = false;
    if (roomCfg.getBillType() == RoomCardBillTypeConstant.BY_GAME) {
      if (roomCfg.getLeftGame() == 0) {
        if (state.equals(RoomCardStateConstant.GOING)) {
          roomCfg.setState(RoomCardStateConstant.OVER);
          roomCfg.setEndTime(new Date());
          isOver = true;
        }
      }
    } else if (roomCfg.getBillType() == RoomCardBillTypeConstant.BY_TIME) {
      long useTime = System.currentTimeMillis() - room.getRoundStartTime().getTime();
      long leftTime = roomCfg.getLeftTime();
      // 剩余时间
      leftTime = leftTime - useTime;
      if (leftTime <= 0) {
        leftTime = 0;
        if (state.equals(RoomCardStateConstant.GOING)) {
          roomCfg.setState(RoomCardStateConstant.OVER);
          isOver = true;
        }
      }
      roomCfg.setLeftTime(leftTime);
    }
    return isOver;
  }

  /**
   * @Title gameOver.
   * @Description 房卡结束
   * @date 2018年6月8日 下午5:42:16
   * @param room .
   */
  public void gameOver(R room) {
    // 游戏状态为over
    room.getRoomCfg().setState(RoomCardStateConstant.OVER);
    // 更新配置到数据库
    RoomCardRoomListDomain record = RoomCardRoomListConvert.INSTANCE.bo2po(room.getRoomCfg());
    roomCardRoomListMapper.updateByPrimaryKeySelective(record);
    // 释放玩家
    removeRoomCard(room.getId());
  }

  /**
   * @Title findPlayerSeat.
   * @Description 获取玩家座位
   * @date 2018年5月10日 下午8:15:04
   * @param playerId .
   * @return .
   */
  public S findPlayerSeat(long playerId) {
    return playerSeats.get(playerId);
  }

  /**
   * @Title findEmptySeat.
   * @Description 查找空位
   * @date 2018年5月28日 下午8:15:47
   * @param player 玩家
   * @param room 房间
   * @param standerBy true：查找所有旁观者座位, false:查找游戏座位
   * @return S 座位
   */
  public S findEmptySeat(Player player, R room, boolean standerBy) {
    T table = room.table();
    S emptySeat = null;
    for (S seat : table.seats()) {
      if (seat.getPlayerId() == 0 && seat.isByStander() == standerBy) {
        emptySeat = seat;
        break;
      }
    }
    return emptySeat;
  }

  /**
   * checkTableSetting.
   * 
   * @Description 校验桌子设置
   * @date 2018年6月14日 下午4:10:26
   * @param player .
   * @param table .
   * @param tablePwd .
   */
  public void checkTableSetting(Player player, T table, String tablePwd) {
    if (table.getPassword() != null && !table.getPassword().equals(tablePwd)) {
      HuohuaAssert.isTrue(false, ErrorCode.GAME_ROOM_PWD_ERROR);
    }

    if (table.getLimitIp() != null && table.getLimitIp().equals(player.ip)) {
      HuohuaAssert.isTrue(false, ErrorCode.GAME_NOT_PLAY_WITH_IP);
    }

    if (player.getGold() < table.getLimitGold()) {
      HuohuaAssert.isTrue(false, ErrorCode.GAME_NOT_PLAY_WITH_MIN_GOLD, "" + table.getLimitGold());
    }
  }

  /**
   * updatePlayerSeat.
   * 
   * @Description 更新玩家位置
   * @date 2018年5月10日 下午8:15:27
   * @param playerId .
   * @param seat .
   */
  public void updatePlayerSeat(long playerId, S seat) {
    playerSeats.put(playerId, seat);
  }

  /**
   * removePlayerSeat.
   * 
   * @Description 移除玩家坐位
   * @date 2018年5月10日 下午8:16:02
   * @param playerId .
   */
  public S removePlayerSeat(long playerId) {
    return playerSeats.remove(playerId);
  }

  /**
   * @Title rooms.
   * @Description 所有房间
   * @author houdongsheng
   * @date 2018年5月10日 下午8:23:06
   * @return .
   */
  public Collection<R> rooms() {
    return rooms.values();
  }

  /**
   * @Title getRoom.
   * @Description 获取特定的房间
   * @author houdongsheng
   * @date 2018年5月10日 下午8:24:01
   * @param roomId 房间编号
   * @return 房卡房间
   */
  public R getRoom(long roomId) {
    return rooms.get(roomId);
  }

  /**
   * @Title createRoom.
   * @Description 创建房间(各游戏自己实现)
   * @author houdongsheng
   * @date 2018年5月10日 下午8:18:25
   * @param room 房卡配置数据
   * @return 房卡房间
   */
  protected abstract R roomCfgToRoom(RCFGT room);

  /**
   * @Title unStartExpire.
   * @Description 游戏未开始过期
   * @date 2018年5月21日 下午4:14:19
   * @param room .
   */
  protected abstract void unStartExpire(R room);
}
